package cn.edu.lcu.cs.javaprogramming.io;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;

import java.io.*;
import java.net.URL;
import java.time.LocalDateTime;

/**
 * 文件IO演示
 * 
 * @author Ling
 *
 */
public class FileIODemo {
	/**
	 * 文件系统中的文件，绝对路径或相对路径
	 */
	private String filename = "myfile.txt";

	@ParameterizedTest
	@ValueSource(strings = "hello.txt")
	public void file(String fileName) throws IOException {
		File file = new File(fileName);
		System.out.println("文件位置：" + file.getAbsolutePath());
		// 判断文件是否存在
		if (file.exists()) {
			System.out.println("文件已存在");
		} else {
			// 如果文件不存在，就创建它
			boolean created = file.createNewFile();
			if (created) {
				System.out.println("文件创建成功");
			}
		}
	}

	@Test
	public void absolutePath() throws IOException {
		File file = new File(".");
		// 绝对地址，可能带有 . ..
		// 如 D:\temp\.\myfile.txt
		System.out.println(file.getAbsolutePath());
		// 简洁的绝对地址，计算好的绝对地址，不含 . ..
		System.out.println(file.getCanonicalPath());
	}

	@Test
	public void noneExistsPath() {
		// 给定一个不存在的路径
		File file = new File("D:/sssss");
		System.out.println(file.exists()); // false
		System.out.println(file.isDirectory()); // false
		System.out.println(file.isFile()); // false
	}

	/**
	 * <p>
	 * File对象演示，包括如何创建目录
	 * </p>
	 * <p>
	 * 文件系统中的绝对路径，不同OS格式不同，如果写死，将来程序跨OS移植就有困难。
	 * </p>
	 * <p>
	 * 虽然Windows系统的路径分隔符是 \ ，但是用 / 更好更通用。</>
	 */
	@ParameterizedTest
	@ValueSource(strings = "D:/a/b/c/d")
	public void makeDir(String absolutePath) {
		boolean isMade = false;
		File folder = new File(absolutePath);
		if (!folder.exists()) {
//			isMade = folder.mkdir(); // 只创建末端文件夹
			isMade = folder.mkdirs(); // 不只创建末端文件夹，如果上级文件夹不存在，一并创建
		} else {
			System.out.println("文件夹已经存在");
		}
		System.out.println(isMade ? "文件夹创建成功" : "文件夹创建失败");
	}

	/**
	 * 读取文本文件
	 * 
	 * @throws IOException
	 */
	@ParameterizedTest
	@ValueSource(strings = "/2020.34.csv")
	public void readTextFile(String classPathFileName) throws IOException {
		// 将类路径中的文件转换为URL
		URL resource = this.getClass().getResource(classPathFileName);
		// 获取绝对路径
		String path = resource.getPath();
		System.out.println("读取文件：" + path);

		// 可以将类路径中的文件直接转换为输入流
//		InputStream inputStream = this.getClass().getResourceAsStream(classPathFileName);

		// 依据获取的绝对路径，创建文件读取器，并包装为带缓冲的读取器
		FileReader reader = new FileReader(path);
		int ch;
		// 一次读取一个字符，但是做为整型返回
		// 如果读取失败，返回-1
		while ((ch = reader.read()) != -1) {
			// 将读取的字符打印到控制台
			System.out.print((char) ch);
			// 也可将读取的字符输出到另外一个文件输出流，实现文件的复制
		}

		reader.close();
	}

	/**
	 * 带缓冲读取文本文件
	 * 
	 * @throws IOException
	 */
	@ParameterizedTest
	@ValueSource(strings = "/cn/edu/lcu/cs/javalearning/io/2019.45.txt")
	public void bufferedReadTextFile(String classPathFileName) throws IOException {
		// 将类路径中的文件转换为URL
		URL resource = this.getClass().getResource(classPathFileName);
		// 获取绝对路径
		String path = resource.getPath();
		System.out.println("读取文件：" + path);

		// 可以将类路径中的文件直接转换为输入流
		// InputStream inputStream =
		// this.getClass().getResourceAsStream(classPathFileName);

		// 依据获取的绝对路径，创建文件读取器，并包装为带缓冲的读取器
		BufferedReader reader = new BufferedReader(new FileReader(path));
		String line;
		// 带缓冲的读取器可以一次读取一行
		while ((line = reader.readLine()) != null) {
			System.out.println(line);
		}

		reader.close();
	}

	/**
	 * 写文件演示。IO流或文件流操作，会抛出一些必检异常，可以不处理，向上抛出。
	 * 
	 * @throws IOException
	 */
	@Test
	public void writeFile() throws IOException {
		// 文件名可以是相对路径或绝对路径
		// File对象既可以是文件，也可以是文件夹
		File file = new File(filename);
		// 如果是相对路径，要搞清楚文件写到哪里去了
		// 获取文件的绝对路径
		String absolutePath = file.getAbsolutePath();

		// 创建字符文件输出流，别忘记及时关闭
		FileWriter writer = new FileWriter(file);
		String content = "我想继续放假！";

		// 向文件流中追加数据，即写入文件
		// append write 功能差不多，参数类型不同而已
		writer.append(LocalDateTime.now().toString());
		writer.write("\t");
		writer.append(content);
		writer.append("\n");
		// 如果程序执行到本行，说明写文件操作成功，控制台上打印文件的绝对路径
		System.out.println("文件写入成功：" + absolutePath);
		// IO流（包含文件流）必须及时关闭
		writer.close();
	}

	/**
	 * 写文件演示。IO流或文件流操作，会抛出一些必检异常，用代码捕获并处理异常。
	 * 
	 */
	@Test
	public void writeFile2() /*throws IOException*/ {
		// 文件名可以是相对路径或绝对路径
		File file = new File(filename);
		FileWriter writer = null;
		try {
			// 创建字符文件输出流，别忘记及时关闭
			writer = new FileWriter(file);
			String content = "我想继续放假！";

			// 向文件流中追加数据，即写入文件
			writer.append(LocalDateTime.now() + "\t" + content + "\n");
			// 如果程序执行到本行，说明写文件操作成功，控制台上打印文件的绝对路径
			System.out.println("文件写入成功：" + file.getAbsolutePath());
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			// 无论有没有异常发生，都保证关闭文件流
			try {
				writer.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 写文件演示。自动关闭文件流。
	 * 
	 */
	@Test
	public void writeFile3() /*throws IOException*/ {
		// 文件名可以是相对路径或绝对路径
		File file = new File(filename);
		String content = "我想继续放假！";

		// try-with-resources：try中声明的变量会被自动关闭，不需要程序员显式关闭
		// 条件：变量类型需要实现Closeable接口
		try (FileWriter writer = new FileWriter(file)) {
			// 向文件流中追加数据，即写入文件
			writer.append(LocalDateTime.now() + "\t" + content + "\n");
			// 如果程序执行到本行，说明写文件操作成功，控制台上打印文件的绝对路径
			System.out.println("文件写入成功：" + file.getAbsolutePath());
			// Writer实现了Closeable接口，而且在try-with-resources中声明，可以被自动关闭
//			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 带缓冲区文本文件复制，目标文件与原始文件相同文件夹
	 * 
	 * @param fromFileName 原始文件名。可指定文件系统路径，或类路径。
	 * @param toFileName   目标文件名，复制到与原始文件相同的文件夹。
	 * @throws IOException
	 */
	@ParameterizedTest
	@CsvSource({ "classpath:/cn/edu/lcu/cs/javalearning/io/2019.45.txt, backup.txt", "D:/2019.45.txt, backup.txt" })
	public void bufferedFileCopy(String fromFileName, String toFileName) throws IOException {
		// 如果指定类路径，将类路径转换为文件系统路径
		String prefix = "classpath:";
		if (fromFileName.toLowerCase().startsWith(prefix)) {
			fromFileName = fromFileName.substring(prefix.length());
			fromFileName = this.getClass().getResource(fromFileName).getPath();
		}

		File fromFile = new File(fromFileName);
		if (!fromFile.exists()) {
			System.out.println("原始文件不存在：" + fromFileName);
			return;
		}
		File toFile = new File(fromFile.getParent(), toFileName);

		int len = 0;
		char[] buffer = new char[1024];

		// 依据获取的绝对路径，创建文件读取器，并包装为带缓冲的读取器
		FileReader reader = new FileReader(fromFile);
		FileWriter writer = new FileWriter(toFile);
		while ((len = reader.read(buffer)) != -1) {
			writer.write(buffer, 0, len);
		}

		reader.close();
		writer.close();
		System.out.println("文件复制成功");
		System.out.println(fromFile.getAbsolutePath() + "\t-->\t" + toFile.getAbsolutePath());
	}
	
	@Test
	public void systemProperties() {
		String property = System.getProperty("line.separator");
		System.out.println(property);
	}
}
